package com.dwl.dbtemplate;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.log4j.Logger;

import com.dwl.dbhelp.exception.DbHelpException;
import com.dwl.dbtemplate.dbutils.BasicRowProcessor;
import com.dwl.dbtemplate.dbutils.CamelBeanProcessor;
import com.dwl.dbtemplate.dbutils.CUDRunner;
import com.dwl.dbtemplate.dbutils.QRunner;
import com.dwl.dbtemplate.dbutils.DbUtils;
import com.dwl.dbtemplate.dbutils.RowProcessor;
import com.dwl.dbtemplate.dbutils.handlers.ArrayHandler;
import com.dwl.dbtemplate.dbutils.handlers.ArrayListHandler;
import com.dwl.dbtemplate.dbutils.handlers.BeanHandler;
import com.dwl.dbtemplate.dbutils.handlers.BeanListHandler;
import com.dwl.dbtemplate.dbutils.handlers.MapHandler;
import com.dwl.dbtemplate.dbutils.handlers.MapListHandler;
import com.dwl.dbtemplate.dbutils.handlers.ScalarHandler;
import com.dwl.dbtemplate.dbutils.handlers.StringArrayHandler;
import com.dwl.dbtemplate.dbutils.handlers.StringArrayListHandler;
import com.dwl.dbtemplate.dbutils.handlers.StringMapHandler;
import com.dwl.dbtemplate.dbutils.handlers.StringMapListHandler;
import com.dwl.dbtemplate.page.ListPage;
import com.dwl.dbtemplate.page.ListPagedStatement;


/***
 * DbTemplate负责所有CURD操作和分页取数。
 * 其中分页取数分了三块，区别为返回的数据类型，List、Map、Bean
 * @author dongw
 *
 */

public class DbTemplate{
	protected static final Logger logger = Logger.getLogger(DbTemplate.class);

	private DataSource dataSource;
	protected String dialect;
	public static final RowProcessor BEAN_ROW_PROCESSOR = new BasicRowProcessor(new CamelBeanProcessor());

	private static QRunner qrun = null;
	
	private static CUDRunner drun = null;
	
	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	
	/**
	 * 初始化CURD 类
	 */
	private void initRunner(){
		qrun = new QRunner(dataSource);
		drun = new CUDRunner(dataSource);
	}
	
	/**
	 * 初始化数据库方言
	 * 不加载此方法查询分页将不能用，其他操作均可
	 * 目前适用mysql和oracle
	 */
	//init dialect 
	public void InitDialect() {
		initRunner();
		try {
			Connection conn = dataSource.getConnection();
			DatabaseMetaData databaseMetaData = conn.getMetaData();
			String databaseName = databaseMetaData.getDatabaseProductName();
			logger.info("got data source to: " + databaseMetaData.getDatabaseProductName() + " ("
					+ databaseMetaData.getDatabaseProductVersion() + ") through driver: "
					+ databaseMetaData.getDriverName() + " (" + databaseMetaData.getDriverVersion() + ")");
			if (databaseName.equalsIgnoreCase("postgresql")) {
				logger.info("selecting postgres implementation.");
				this.dialect = "postgresql";
			} else if (databaseName.equalsIgnoreCase("mysql")) {
				logger.info("selecting mysql implementation.");
				this.dialect = "mysql";
			} else if (databaseName.equalsIgnoreCase("oracle")) {
				logger.info("selecting oracle implementation.");
				this.dialect = "oracle";
			} else if (databaseName.equalsIgnoreCase("hsql database engine")) {
				logger.info("selecting hsql implementation.");
				this.dialect = "hsql";
			} else {
				logger.warn("unknown database '" + databaseName
						+ "', will use generic driver, which does not support paging result sets. All data will be read on all select statements!");
				this.dialect = "oracle";
			}
		} catch (SQLException e) {
			throw new DbHelpException("could not instantiate database dialect", e);
		}
	}

	//事物操作类，脱离spring的时候可以使用
	public DbTransaction getDbTransaction() {
		return new DbTransaction(dataSource);
	}

	/**
	 * 获取connection
	 * @return
	 */
	private synchronized Connection getConn() {
		try {
			return dataSource.getConnection();
		} catch (SQLException e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}

	/**
	 * 关闭数据库连接
	 * @param conn
	 */
	private void closeConn(Connection conn) {
		DbUtils.closeQuietly(conn);
	}

	/***
	 * 
	 * 查询返回Object，没经过测试，别人提供的代码
	 * 
	 * @param sql 入参sql语句
	 * @param params SQL语句的参数
	 * @return
	 * @throws SQLException
	 */
	public String queryScalar(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		Object obj = qrun.query(sql, new ScalarHandler(1), params);
		if (obj != null) {
			return obj.toString();
		}
		return null;
	}

	/***
	 * 根据SQL语句和参数进行批量更新
	 * @param sql
	 * @param params
	 * @return
	 * @throws SQLException
	 */
	
	public int[] batch(String sql, List<Object[]> params) throws SQLException {
		debug(sql, new Object[] { "batch sql, count:" + (params == null ? -1 : params.size()) });
		if (params == null) {
			throw new SQLException("Null parameters. If parameters aren't need, pass an empty array.");
		}
		Object[][] o = new Object[params.size()][];
		for (int i = 0; i < params.size(); i++) {
			o[i] = ((Object[]) params.get(i));
		}
		return drun.batch(sql, o);
	}

	/***
	 * 根据SQL语句查询返回Map<String, String>类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public Map<String, String> getMap(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		return (Map) qrun.query(sql, new StringMapHandler(), params);
	}

	/***
	 * 根据SQL语句查询返回Map<String, Object>类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public Map<String, Object> getNativeMap(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		
		return (Map) qrun.query(sql, new MapHandler(), params);
	}
	/***
	 * 根据SQL语句查询返回String[]类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public String[] getArray(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		
		return (String[]) qrun.query(sql, new StringArrayHandler(), params);
	}
	/***
	 * 根据SQL语句查询返回Object[]类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public Object[] getNativeArray(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		
		return (Object[]) qrun.query(sql, new ArrayHandler(), params);
	}
	/***
	 * 根据SQL语句查询返回Bean实体类
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public <T> T getBean(String sql, Class<T> type, Object[] params) throws SQLException {
		debug(sql, params);
		
		return (T) qrun.query(sql, new BeanHandler(type, BEAN_ROW_PROCESSOR), params);
	}
	/***
	 * 根据SQL语句查询返回List<Map<String, String>>类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public List<Map<String, String>> getMapList(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		return (List) qrun.query(sql, new StringMapListHandler(), params);
	}
	/***
	 * 根据SQL语句查询返回List<Map<String, Object>>类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public List<Map<String, Object>> getNativeMapList(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		return (List) qrun.query(sql, new MapListHandler(), params);
	}
	/***
	 * 根据SQL语句查询返回List<String[]>类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public List<String[]> getArrayList(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		return (List) qrun.query(sql, new StringArrayListHandler(), params);
	}
	/***
	 * 根据SQL语句查询返回List<Object[]>类型结果集
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public List<Object[]> getNativeArrayList(String sql, Object[] params) throws SQLException {
		debug(sql, params);
		return (List) qrun.query(sql, new ArrayListHandler(), params);
	}
	/***
	 * 根据SQL语句查询返回List<T>  对象集合
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public <T> List<T> getBeanList(String sql, Class<T> type, Object[] params) throws SQLException {
		debug(sql, params);
		return (List) qrun.query(sql, new BeanListHandler(type, BEAN_ROW_PROCESSOR), params);
	}
	
	/***
	 * 根据SQL语句查询返回ListPage<String[]> 默认查前20条
	 * @param sql
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public ListPage<String[]> getArrayPage(String sql) throws SQLException {
		return getArrayPage(sql, 1, 20, null);
	}

	/***
	 * 根据SQL语句查询返回ListPage<String[]> 返回curPage后20条
	 * @param sql
	 * @param curPage 起始位置
	 * @return
	 * @throws SQLException
	 */
	public ListPage<String[]> getArrayPage(String sql, int curPage) throws SQLException {
		return getArrayPage(sql, curPage, 20, null);
	}

	/***
	 * 根据SQL语句查询返回ListPage<String[]> 返回给定的curPage位置后pageSize（条数）。
	 * @param sql
	 * @param curPage 起始位置
	 * @param pageSize 结果集数量
	 * @return
	 * @throws SQLException
	 */
	public ListPage<String[]> getArrayPage(String sql, int curPage, int pageSize) throws SQLException {
		return getArrayPage(sql, curPage, pageSize, null);
	}

	/***
	 * 根据SQL语句查询返回ListPage<String[]> 返回给定的curPage位置后pageSize（条数）。
	 * @param sql
	 * @param curPage起始位置
	 * @param pageSize结果集数量
	 * @param params SQL语句参数
	 * @return
	 * @throws SQLException
	 */
	public ListPage<String[]> getArrayPage(String sql, int curPage, int pageSize, Object[] params) throws SQLException {
		ListPagedStatement pst = new ListPagedStatement(dataSource, this.dialect, sql, curPage, pageSize, params);
		return pst.executeQuery(0);
	}
	/***
	 * 根据SQL语句查询返回ListPage<Map<String, Object>> 默认查询前20条数据
	 * @param sql
	 * @return
	 * @throws SQLException
	 */
	public ListPage<Map<String, Object>> getNativeMapPage(String sql) throws SQLException {
		return getNativeMapPage(sql, 1, 20, null);
	}
	/***
	 * 根据SQL语句和起始位置查询返回ListPage<Map<String, Object>> 结果集，数量为20.
	 * @param sql
	 * @param curPage查询起始位置
	 * @return
	 * @throws SQLException
	 */
	public ListPage<Map<String, Object>> getNativeMapPage(String sql, int curPage) throws SQLException {
		return getNativeMapPage(sql, curPage, 20, null);
	}
	/***
	 * 根据SQL语句、起始位置和查询数量查询返回ListPage<Map<String, Object>>结果集，
	 * @param sql
	 * @param curPage起始位置
	 * @param pageSize查询数量
	 * @return
	 * @throws SQLException
	 */
	public ListPage<Map<String, Object>> getNativeMapPage(String sql, int curPage, int pageSize) throws SQLException {
		return getNativeMapPage(sql, curPage, pageSize, null);
	}
	/***
	 * 根据SQL语句、起始位置、查询数量和SQL语句参数查询返回ListPage<Map<String, Object>> 
	 * @param sql SQL语句
	 * @param curPage 起始位置
	 * @param pageSize 查询数量
	 * @param params 参数
	 * @return
	 * @throws SQLException
	 */
	public ListPage<Map<String, Object>> getNativeMapPage(String sql, int curPage, int pageSize, Object[] params)
			throws SQLException {
		ListPagedStatement pst = new ListPagedStatement(dataSource, this.dialect, sql, curPage, pageSize, params);
		return pst.executeQuery(2);
	}

	/***
	 * 根据SQL语句查询返回前20条数据结果集，返回类型Bean
	 * @param sql
	 * @param clazz
	 * @return
	 * @throws SQLException
	 */
	public <T> ListPage<T> getPage(String sql, Class<T> clazz) throws SQLException {
		return getPage(sql, clazz, 1, 20, null);
	}

	/***
	 * 根据SQL语句和起始位置查询返回ListPage<Bean> 结果集，数量为20.
	 * @param sql
	 * @param clazz
	 * @param curPage
	 * @return
	 * @throws SQLException
	 */
	public <T> ListPage<T> getPage(String sql, Class<T> clazz, int curPage) throws SQLException {
		return getPage(sql, clazz, curPage, 20, null);
	}
	/***
	 * 根据SQL语句、起始位置和查询数量查询返回ListPage<T>结果集，
	 * @param sql
	 * @param clazz
	 * @param curPage
	 * @param pageSize
	 * @return
	 * @throws SQLException
	 */
	public <T> ListPage<T> getPage(String sql, Class<T> clazz, int curPage, int pageSize) throws SQLException {
		return getPage(sql, clazz, curPage, pageSize, null);
	}

	/***
	 * 根据SQL语句、起始位置、查询数量和SQL语句参数查询返回ListPage<T>
	 * @param sql
	 * @param clazz
	 * @param curPage
	 * @param pageSize
	 * @param params
	 * @return
	 * @throws SQLException
	 */
	public <T> ListPage<T> getPage(String sql, Class<T> clazz, int curPage, int pageSize, Object[] params)
			throws SQLException {
		ListPagedStatement pst = new ListPagedStatement(dataSource, this.dialect, sql, curPage, pageSize, params);
		pst.setClazz(clazz);
		return pst.executeQuery(-1);
	}
	/***
	 * 查询前20条数据  返回类型ListPage<Map<String, String>>
	 * @param sql
	 * @return
	 * @throws SQLException
	 */
	public ListPage<Map<String, String>> getMapPage(String sql) throws SQLException {
		return getMapPage(sql, 1, 20, null);
	}
	/***
	 * 根据SQL语句和当前位置查询去20条数据  
	 * @param sql
	 * @param curPage
	 * @return ListPage<Map<String, String>>
	 * @throws SQLException
	 */
	public ListPage<Map<String, String>> getMapPage(String sql, int curPage) throws SQLException {
		return getMapPage(sql, curPage, 20, null);
	}

	/***
	 * 根据SQL语句、当前位置、返回条数查询返回结果集
	 * @param sql
	 * @param curPage
	 * @param pageSize
	 * @return ListPage<Map<String, String>>
	 * @throws SQLException
	 */
	public ListPage<Map<String, String>> getMapPage(String sql, int curPage, int pageSize) throws SQLException {
		return getMapPage(sql, curPage, pageSize, null);
	}

	/***
	 * 根据SQL语句、参数、当前查询位置和查询返回条数查询返回ListPage<Map<String, String>>
	 * @param sql
	 * @param curPage
	 * @param pageSize
	 * @param params
	 * @return ListPage<Map<String, String>>
	 * @throws SQLException
	 */
	public ListPage<Map<String, String>> getMapPage(String sql, int curPage, int pageSize, Object[] params)
			throws SQLException {
		ListPagedStatement pst = new ListPagedStatement(dataSource, this.dialect, sql, curPage, pageSize, params);
		return pst.executeQuery(1);
	}

	/***
	 * 日志调试
	 * @param sql
	 * @param params
	 */
	protected void debug(String sql, Object[] params) {
		if (true) {
			if (params == null) {
				params = new Object[0];
			}
			StringBuilder sb = new StringBuilder(400);
			sb.append(sql);
			if (params.length != 0) {
				sb.append(" [params:");
				for (Object obj : params) {
					sb.append(obj == null ? "<null>" : obj.toString()).append(", ");
				}
				sb.setLength(sb.length() - 2);
				sb.append("]");
			}
			logger.info(sb.toString());
		}
	}
	/***
	 * 根据SQL语句和参数进行更新
	 * @param sql
	 * @param List<Object> params
	 * @return
	 */
	public int update(String sql, List<Object> params) {
		
		return drun.update(getConn(), sql, params);
	}
	/***
	 * 根据SQL语句和参数进行更新
	 * @param sql
	 * @param Object[] params
	 * @return
	 */
	public int update(String sql, Object[] params) {
		
		return drun.update(getConn(), sql, params);
	}
	/***
	 * 根据Bean进行插入数据
	 * @param cls
	 * @param bean
	 * @return
	 */
	public <T> int insert(Class<T> cls, T bean) {
		
		return drun.insert(getConn(), cls, bean);
	}
	/***
	 * 根据list<Bean>插入数据
	 * @param cls
	 * @param beans
	 * @return
	 */
	public <T> int[] insert(Class<T> cls, List<T> beans) {
		
		return drun.insert(getConn(), cls, beans);
	}

	/***
	 * 根据Bean更新,默认主键为id，如果主键不为id，则使用update(Class<T> cls, T bean, String primaryKey)
	 * @param cls
	 * @param bean
	 * @return
	 */
	public <T> int update(Class<T> cls, T bean) {
		
		return drun.update(getConn(), cls, bean);
	}
	/***
	 * 提供主键字段名进行更新
	 * @param cls
	 * @param bean
	 * @param primaryKey
	 * @return
	 */
	public <T> int update(Class<T> cls, T bean, String primaryKey) {
		
		return drun.update(getConn(),cls, bean, primaryKey);
	}
	/***
	 * 根据list<Bean>进行更新 默认主键为id 如果主键不为id，则使用 update(Class<T> cls, List<T> beans, String primaryKey) 
	 * @param cls
	 * @param beans
	 * @return
	 */
	public <T> int[] update(Class<T> cls, List<T> beans) {
		
		return drun.update(getConn(),cls, beans);
	}
	
	/***
	 * 提供主键字段名进行更新
	 * @param cls
	 * @param bean
	 * @param primaryKey
	 * @return
	 */
	public <T> int[] update(Class<T> cls, List<T> beans, String primaryKey) {
		
		return drun.update(getConn(),cls, beans, primaryKey);
	}
	/***
	 * 根据id删除数据
	 * @param cls
	 * @param id
	 * @return
	 */
	public <T> int delete(Class<T> cls, long id) {
		
		return drun.delete(getConn(), cls, id);
	}
}

